﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Fill Modes
    /// </summary>
    public enum FILL_MODE
    {
        /// <summary>
        /// 
        /// </summary>
        LOCATIONS, 

        /// <summary>
        /// 
        /// </summary>
        HOLES,

        /// <summary>
        /// 
        /// </summary>
        LOCATIONS_CONNECTIONS
    }

    /// <summary>
    /// BW2 = imfill(BW) displays the binary image BW on the screen and lets you define the region to fill by selecting points interactively by using the mouse. To use this interactive syntax, BW must be a 2-D image. Press Backspace or Delete to remove the previously selected point. A shift-click, right-click, or double-click selects a final point and starts the fill operation. Pressing Return finishes the selection without adding a point.
    /// [BW2,locations] = imfill(BW) returns the locations of points selected interactively in locations. locations is a vector of linear indices into the input image. To use this interactive syntax, BW must be a 2-D image.
    /// BW2 = imfill(BW,locations) performs a flood-fill operation on background pixels of the binary image BW, starting from the points specified in locations. If locations is a P-by-1 vector, it contains the linear indices of the starting locations. If locations is a P-by-ndims(BW) matrix, each row contains the array indices of one of the starting locations.
    /// BW2 = imfill(BW,'holes') fills holes in the binary image BW. A hole is a set of background pixels that cannot be reached by filling in the background from the edge of the image.
    /// I2 = imfill(I) fills holes in the grayscale image I. In this syntax, a hole is defined as an area of dark pixels surrounded by lighter pixels.
    /// BW2 = imfill(BW,locations,conn) fills the area defined by locations, where conn specifies the connectivity. conn can have any of the following scalar values.
    /// </summary>
    public class ImageFill : BaseImageFilter
    {
        protected IEnumerable<Point> m_aPoint = null;
        protected FILL_MODE M_FILL_MODE = FILL_MODE.HOLES;
        protected IFilter m_FloodFillFilter = new FloodFillFilter();

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageFill"/> class.
        /// </summary>
        public ImageFill()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageFill"/> class.
        /// </summary>
        /// <param name="_fillMode">The _fill mode.</param>
        public ImageFill(FILL_MODE _fillMode)
        {
            M_FILL_MODE = _fillMode;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageFill"/> class.
        /// </summary>
        /// <param name="_aPoint">The _a point.</param>
        public ImageFill(IEnumerable<Point> _aPoint)
        {
            m_aPoint = _aPoint;
        }

        /// <summary>
        /// Initializes a new instance of the ImageFill class.
        /// </summary>
        /// <param name="_imfill">The _imfill.</param>
        internal ImageFill(ImageFill _imfill)
        {
        }

        /// <summary>
        /// Gets or sets the fill mode.
        /// </summary>
        /// <value>The fill mode.</value>
        public FILL_MODE FillMode
        {
            get { return M_FILL_MODE; }
            set { M_FILL_MODE = value; }
        }

        /// <summary>
        /// Gets the points.
        /// </summary>
        /// <value>The points.</value>
        public IEnumerable<Point> Points
        {
            get { return m_aPoint; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;
            UnsafeGraphics g = null;

            int x = 0;
            int y = 0;
            switch (M_FILL_MODE)
            {
                case FILL_MODE.LOCATIONS:
                    m_FloodFillFilter.Execute(_bitmap);
                    break;

                case FILL_MODE.HOLES:
                    UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();
                    g = UnsafeGraphics.FromImage(bitmapCloned);
                    g.FloodFill(new Point(0, 0), Definitions.White);

                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            if (bitmapCloned.GetPixel(x, y) == Definitions.Black)
                            {
                                _bitmap.SetPixel(x, y, Definitions.White);
                            }
                        }
                    }
                    bitmapCloned.Dispose();
                    break;

                    //This is not implemented yet, but will
                case FILL_MODE.LOCATIONS_CONNECTIONS:
                    g = UnsafeGraphics.FromImage(_bitmap);
                    foreach(Point point in m_aPoint)
                    {
                        g.FloodFill(point, Definitions.White);
                    }
                    break;
            }
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ImageFill(this);
        }
    }
}
